﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using HydroDesktop.Interfaces.ObjectModel;
using System.IO;
using System.Diagnostics;
using HydroDesktop.Interfaces;
using DotSpatial.Controls;
using HydroDesktop.Interfaces.PluginContracts;

namespace DroughtAnalysis
{
    public partial class SelectStationForm : Form
    {
        #region Fields

        private ISeriesSelector _seriesView;
        private Map _mainMap;
        private readonly ISearchPlugin _searchPlugin;

        #endregion

        /// <summary>
        /// Creates a new instance of the parameter selection form
        /// </summary>
        public SelectStationForm(DroughtSettings settings, ISeriesSelector seriesView, Map mainMap, ISearchPlugin searchPlugin)
        {
            InitializeComponent();

            _seriesView = seriesView;
            _mainMap = mainMap;
            _searchPlugin = searchPlugin;

            Settings = settings;
            progBar.Visible = false;

            dgvInput.AutoGenerateColumns = false;
            dgvOutput.AutoGenerateColumns = false;
        }


        /// <summary>
        /// Gets or sets the drought calculation parameter settings associated with this form
        /// </summary>
        public DroughtSettings Settings { get; set; }

        private string defaultOutputFolder;

        private void SelectStationForm_Load(object sender, EventArgs e)
        {
            //bindingSource1.DataSource = Settings.SuitableSites;

            PopulateStationList();

            PopulateDates();

            PopulateMethods();

            TryGuessPathToR();

            TryCreateDefaultOutputFolder();
            defaultOutputFolder = Settings.OutputDirectory;
        }

        private void PopulateStationList()
        {
            foreach (Site s in Settings.SuitableSites)
            {
                checkListBoxStations.Items.Add(s);
            }
            if (checkListBoxStations.Items.Count > 0)
            {
                btnCheckAll.Enabled = true;
                btnUncheckAll.Enabled = false;
            }
            lblNumSelected.Text = String.Format("{0} out of {1} stations selected.", 
                checkListBoxStations.CheckedItems.Count, checkListBoxStations.Items.Count);
        }

        private void PopulateDates()
        {
            dtpStartDate.Value = Settings.StartDate;
            dtpEndDate.Value = Settings.EndDate;
        }

        /// <summary>
        /// Populates the drought calculation methods by adding names of scripts
        /// </summary>
        private void PopulateMethods()
        {
            Settings.DroughtMethods = new DroughtMethodManager().GetDroughtMethodsFromDirectory().ToList<DroughtMethodInfo>();
            comboBoxMethod.DataSource = Settings.DroughtMethods;
            comboBoxMethod.SelectedIndex = 0;
        }

        private void TryGuessPathToR()
        {
            try
            {
                string programFilesDir = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
                if (programFilesDir.EndsWith(" (x86)"))
                {
                    programFilesDir = programFilesDir.Remove(programFilesDir.IndexOf(" (x86)"));
                }
                string baseRPath1 = "R\\R-{0}\\bin\\i386\\R.exe";
                string[] possibleRVersionsNew = new string[] { "2.14.3", "2.14.2", "2.14.1", "2.14.0", "2.13.2", "2.13.1", "2.13.0", "2.12.2", "2.12.1", "2.12.0", };
                string baseRPathOld = "R\\R-{0}\\bin\\R.exe";
                string[] possibleRVersionsOld = new string[] { "2.11.1", "2.11.0", "2.10.1", "2.10.0" };
                foreach (string rVersion in possibleRVersionsNew)
                {
                    string rPath = Path.Combine(programFilesDir, string.Format(baseRPath1, rVersion));
                    if (System.IO.File.Exists(rPath))
                    {
                        Settings.PathToR = rPath;
                        txtPathToR.Text = rPath;
                        return;
                    }
                }
                foreach (string rVersion in possibleRVersionsOld)
                {
                    string rPath = Path.Combine(programFilesDir, string.Format(baseRPathOld, rVersion));
                    if (System.IO.File.Exists(rPath))
                    {
                        Settings.PathToR = rPath;
                        txtPathToR.Text = rPath;
                        return;
                    }
                }

            }
            catch (UnauthorizedAccessException)
            {
                return;
            }
        }

        private void TryCreateDefaultOutputFolder()
        {
            try
            {
                string documentsDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
                string hydroDocsDir = Path.Combine(documentsDir, "HydroDesktop");
                if (!Directory.Exists(hydroDocsDir))
                {
                    Directory.CreateDirectory(hydroDocsDir);
                }
                if (Directory.Exists(hydroDocsDir))
                {
                    string droughtDir = Path.Combine(hydroDocsDir, "Drought");
                    if (!Directory.Exists(droughtDir))
                    {
                        Directory.CreateDirectory(droughtDir);
                    }

                    if (Directory.Exists(droughtDir))
                    {
                        Settings.OutputDirectory = droughtDir;
                        txtOutputFolder.Text = Settings.OutputDirectory;
                        defaultOutputFolder = droughtDir;
                    }
                }

                

            }
            catch (UnauthorizedAccessException)
            {
                return;
            }
            catch (IOException)
            {
                return;
            }

        }


        private void btnPathToR_Click(object sender, EventArgs e)
        {
            
            OpenFileDialog fd = new OpenFileDialog();
            string initialDir = @"C:\Program Files\R";
            if (Directory.Exists(initialDir))
            {
                fd.InitialDirectory = initialDir;
            }
            fd.Filter = "R.exe file|*.exe";

            if (fd.ShowDialog() == DialogResult.OK)
            {
                Settings.PathToR = fd.FileName;
                txtPathToR.Text = Settings.PathToR;
            }
        }

        private void TextBoxR_TextChanged(object sender, EventArgs e)
        {
            if (txtPathToR.Text.ToLower().EndsWith("r.exe"))
            {
                Settings.PathToR = txtPathToR.Text;
            }
        }

        private void btnOutputFolder_Click(object sender, EventArgs e)
        {
           FolderBrowserDialog fd = new FolderBrowserDialog();
           if (Settings.OutputDirectory != null)
               if (Directory.Exists(Settings.OutputDirectory))
                   fd.SelectedPath = Settings.OutputDirectory;

            if (fd.ShowDialog() == DialogResult.OK)
            {
                fd.ShowNewFolderButton = true;
                Settings.OutputDirectory = fd.SelectedPath;
                txtOutputFolder.Text = Settings.OutputDirectory;
            }
        }

        private void TextBoxOutputFolder_TextChanged(object sender, EventArgs e)
        {
            if (Directory.Exists(txtOutputFolder.Text))
            {
                Settings.OutputDirectory = txtOutputFolder.Text;
            }
        }

        //analyze drought button
        private void btnAnalyzeDrought_Click(object sender, EventArgs e)
        {
            mainTabControl.SelectedTab = mainTabControl.TabPages["tabProgress"];
            
            Settings.SelectedSites.Clear();
            foreach (Site s in checkListBoxStations.CheckedItems)
            {
                Settings.SelectedSites.Add(s);
            }
            
            if (Settings.SelectedSites.Count == 0)
            {
                MessageBox.Show("Please select a station.");
                return;
            }
            if (!Directory.Exists(Settings.OutputDirectory))
            {
                MessageBox.Show("Please specify a valid output directory.");
                return;
            }

            //set the output directory
            Settings.OutputDirectory = txtOutputFolder.Text;

            //(1) Create data files for all selected sites
            progBar.Visible = true;
            bgWorker.RunWorkerAsync(new RScriptCalculationInput { CalculationStage = "export" });         
        }

        

        private void btnViewResults_Click(object sender, EventArgs e)
        {
            try
            {
                System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo()
                {
                    FileName = Settings.OutputDirectory,
                    UseShellExecute = true,
                    Verb = "open"
                });
            }
            catch (Exception ex)
            {
                MessageBox.Show("Cannot open results folder. " + ex.Message);
            }

        }

        private void Stations_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            this.BeginInvoke(new MethodInvoker(SetCheckedStationsInfo), null);
        }

        private void SetCheckedStationsInfo()
        {
            if (checkListBoxStations.Items.Count == checkListBoxStations.CheckedItems.Count)
            {
                btnCheckAll.Enabled = false;
            }
            else if (checkListBoxStations.CheckedItems.Count == 0)
            {
                btnUncheckAll.Enabled = false;
            }
            else
            {
                btnCheckAll.Enabled = true;
                btnUncheckAll.Enabled = true;
            }
            lblNumSelected.Text = String.Format("{0} out of {1} stations selected.",
                checkListBoxStations.CheckedItems.Count, checkListBoxStations.Items.Count);
        }

        private void CheckAll_Click(object sender, EventArgs e)
        {
            for (int i=0; i< checkListBoxStations.Items.Count; i++)
            {
                checkListBoxStations.SetItemChecked(i, true);
            }
            SetCheckedStationsInfo();
            btnUncheckAll.Enabled = true;
        }

        private void UncheckAll_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < checkListBoxStations.Items.Count; i++)
            {
                checkListBoxStations.SetItemChecked(i, false);
            }
            SetCheckedStationsInfo();
            btnCheckAll.Enabled = true;
        }

        private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            //pass in specific argument that the task is script generation
            RScriptCalculationInput arg = e.Argument as RScriptCalculationInput;

            if (arg != null)
            {
                switch (arg.CalculationStage)
                {
                    case "export":
                        Settings.StartDate = dtpStartDate.Value;
                        Settings.EndDate = dtpEndDate.Value;             
                        DataExporter exp = new DataExporter();
                        exp.CreateInputFilesForR(Settings.SelectedSites, Settings.StartDate, Settings.EndDate, 
                        Settings.OutputDirectory, Settings.ScriptFileName, Settings.SelectedDroughtMethod, bgWorker, e);
                        break;
                    case "calculate":
                        RScriptRunner.ExecuteRScript(arg.PathToR, arg.ScriptFileName, arg.StationInfoList, bgWorker, e);
                        break;
                    case "save":
                    case "database":
                        DatabaseWriter dbWriter = new DatabaseWriter();
                        dbWriter.SaveResultsToDB(arg.StationInfoList, Settings.SelectedDroughtMethod, bgWorker, e);
                        break;
                }
            }
        }

        private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progBar.Value = e.ProgressPercentage;
            lblProgress.Text = (string)e.UserState;

            //set progress log..
            lbProgress.Items.Add((string)e.UserState);
        }

        private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            // case of an error
            if (e.Error != null)
            {
                MessageBox.Show("Error in drought calculation: " + e.Error.Message);
                return;
            }
            
            // case 1: R-script generation completed
            RScriptGenerationResult result1 = e.Result as RScriptGenerationResult;
            if (result1 != null)
            {
                if (!result1.IsSuccessful)
                {
                    MessageBox.Show("Error generating R script and data files.");
                    return;
                }
                else
                {           
                    //R script and data files generated -> Run Rscript.exe process
                    string scriptFile = result1.RScriptFileName;

                    var arg = new RScriptCalculationInput { PathToR = Settings.PathToR, ScriptFileName = scriptFile, StationInfoList = result1.DroughtStationList };
                    arg.CalculationStage = "calculate";

                    bgWorker.RunWorkerAsync(arg);
                    return;
                }
            }

            // case 2: R-script execution completed
            RScriptCalculationResult result2 = e.Result as RScriptCalculationResult;
            if (result2 != null)
            {
                if (result2.IsSuccessful)
                {
                    if (checkBoxWriteToDB.Checked)
                    {
                        var arg = new RScriptCalculationInput { CalculationStage = "save" };
                        arg.StationInfoList = result2.DroughtStationList;
                        bgWorker.RunWorkerAsync(arg);
                    }
                }
                
                else
                {
                    MessageBox.Show(result2.ErrorMessage);
                    return;
                }
            }
            DatabaseSavingResult result3 = e.Result as DatabaseSavingResult;
            if (result3 != null)
            {
                //adding sites to map
                if (_mainMap != null)
                {
                    var manager = new ThemeManager(_searchPlugin);
                    manager.RefreshAllThemes(_mainMap);
                    _seriesView.SetupDatabase();
                }

                MessageBox.Show(result3.StatusMessage);
                return;
            }
        }

        private void checkBoxNewDirectory_CheckedChanged(object sender, EventArgs e)
        {

        }

        private void checkBoxWriteToDB_CheckedChanged(object sender, EventArgs e)
        {

        }

        private void label4_Click(object sender, EventArgs e)
        {

        }

        private void comboBoxMethod_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (comboBoxMethod.SelectedIndex < 0) 
            {
                Settings.ScriptFileName = String.Empty;
                return;

            }
            
            DroughtMethodInfo selectedMethod = (DroughtMethodInfo)comboBoxMethod.SelectedItem;
            Settings.SelectedDroughtMethod = selectedMethod;
            Settings.ScriptFileName = selectedMethod.RScriptPath;

            dgvInput.DataSource = selectedMethod.InputVariablesView;
            dgvOutput.DataSource = selectedMethod.OutputVariablesView;
            txtMethodDescription.Text = selectedMethod.MethodDescription;
            dgvInput.ClearSelection();
            dgvOutput.ClearSelection();
        }

        private void lblDroughtOutput_Click(object sender, EventArgs e)
        {

        }

        /// <summary>
        /// Add a drought method from an R script
        /// </summary>
        /// <param name="sender">the r script</param>
        /// <param name="e">the parameter</param>
        private void btnAddMethod_Click(object sender, EventArgs e) {
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Title = "Select the R Script";
            dialog.Filter = "R-script (*.R)|*.R|All files (*.*)|*.*";

            if (dialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    //check if this is a valid script
                    DroughtMethodInfo info = DroughtMethodInfo.FromRScript(dialog.FileName);

                    if (!info.IsValidMethod())
                    {
                        string inputMessage = (info.InputVariables.Any() ? String.Empty : 
                            "Input parameters not found. Please specify parameters in start of script:\n" + 
                            "@input_variables\n"+"@input_units\n"+ "@input_intervals\n" );

                        string outputMessage = (info.OutputVariables.Any() ? String.Empty :
                            "Output parameters not found. Please specify parameters in start of script:\n" +
                            "@output_variables\n" + "@output_units\n" + "@output_intervals\n");

                        MessageBox.Show ("Error opening R-Script " + Path.GetFileName(dialog.FileName) + ".\n" +
                            inputMessage + outputMessage);
                        return;
                    }

                    //add to settings
                    Settings.DroughtMethods.Add(info);

                    //re-populate list
                    comboBoxMethod.DataSource = null;
                    comboBoxMethod.DataSource = Settings.DroughtMethods;
                    comboBoxMethod.SelectedIndex = comboBoxMethod.Items.Count - 1;
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Error opening R-Script: " + ex.Message);
                    return;
                }

            }
        }
    }
}
